#pragma once
#include "Common.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h>

using namespace LogModule;

const static int defaultsockfd = -1;
const static int backlog = 8;

// 服务器往往是禁止拷贝的
class TcpServer : public NoCopy
{
public:
    TcpServer(uint16_t port)
        : _port(port),
          _listensockfd(defaultsockfd),
          _isrunning(false)
    {
    }

    void Init()
    {
        // 捕捉SIGCHLD信号，进行该信号的忽略，这样父进程就不需要进行等待了
        // signal(SIGCHLD, SIG_IGN);

        // 1.创建套接字
        _listensockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket error";
            exit(SOCKET_ERR);
        }
        LOG(LogLevel::INFO) << "socket success: " << _listensockfd; // 3

        // 2. bind绑定
        InetAddr local(_port);
        int n = bind(_listensockfd, local.NetAddrPtr(), local.NetAddrLen());
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "bind error";
            exit(BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind success" << _listensockfd; // 3

        // 3.设置socket状态为listen
        n = listen(_listensockfd, backlog);
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "listen error";
            exit(LISTEN_ERR);
        }
        LOG(LogLevel::INFO) << "listen success" << _listensockfd; // 3
    }

    // 线程小类
    class ThreadData
    {
    public:
        ThreadData(int fd, InetAddr &ar, TcpServer *s)
            : sockfd(fd),
              addr(ar),
              tsvr(s)
        {
        }

    public:
        int sockfd;
        InetAddr addr;
        TcpServer *tsvr;
    };

    // 提供服务的方法
    // 长服务：多线程多进程比较合适
    void Service(int sockfd, InetAddr &peer)
    {
        char buffer[1024];
        while (true)
        {
            // 1. 先读取数据
            //  a. n>0：读取成功
            //  b. n<0：读取失败
            //  c. n==0：对端把连接关了，读到了文件的结尾\0
            // 这和管道很像
            ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
                buffer[n] = 0; // 设置为c风格的字符串，n<=sizeof(buffer)-1
                LOG(LogLevel::DEBUG) << peer.StringAddr() << " say#" << buffer;

                // 2. 写回数据
                std::string echo_string = "echo# ";
                echo_string += buffer;
                write(sockfd, echo_string.c_str(), echo_string.size());
            }
            else if (n == 0)
            {
                LOG(LogLevel::DEBUG) << peer.StringAddr() << "退出了...";
                // 对端退出了，我也退出
                close(sockfd);
                break;
            }
            else
            {
                // 客户端读取异常了
                LOG(LogLevel::DEBUG) << peer.StringAddr() << "异常...";
                // 退出
                close(sockfd);
                break;
            }
        }
    }

    // routine类内方法，由this指针，所以在这设计成静态的
    static void *Routine(void *args)
    {
        // 先进行线程的分离，使得线程不需要等待
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(args);
        td->tsvr->Service(td->sockfd, td->addr);
        delete td;
        return nullptr;
    }

    void Run()
    {
        _isrunning = true;
        while (_isrunning)
        {
            // a. 获取连接
            struct sockaddr_in peer;
            memset(&peer, 0, sizeof(peer));
            socklen_t len = sizeof(peer);
            // 如果没有连接到来，accept就会阻塞
            int sockfd = accept(_listensockfd, (struct sockaddr *)&peer, &len);
            if (sockfd < 0)
            {
                LOG(LogLevel::WARNING) << "accept error,continue next";
                continue;
            }

            InetAddr addr(peer);

            LOG(LogLevel::INFO) << "accept success,fd peer addr: " << sockfd << " " << addr.StringAddr();

            // version0 ---test version——单进程程序——不会存在
            // Service(sockfd, addr);

            // version1 ---多进程版本
            // pid_t id = fork();
            // if (id < 0)
            // {
            //     LOG(LogLevel::FATAL) << "fork error";
            //     exit(FOCK_ERR);
            // }
            // else if (id == 0)
            // {
            //     // 子进程，子进程除了看到sockfd，能看到listensockfd吗？
            //     // 答案是可以的，但是我们不想让子进程访问listensock
            //     // 所以得让子进程关闭listensock
            //     close(_listensockfd);
            //     if (fork() > 0) // 让子进程创建孙子进程，然后自己退出
            //     {
            //         exit(OK);
            //     }
            //     // 让孙子进程处理IO请求
            //     // 孙子进程由于子进程退出了，变成了孤儿进程，由系统回收
            //     Service(sockfd, addr);
            //     // 然后让子进程处理完任务后直接退出
            //     exit(OK);
            // }
            // else
            // {
            //     // 父进程
            //     close(sockfd);

            //     // 父进程进行等待，这个时候由于我们让子进程退出了，所以不会阻塞
            //     pid_t rid = waitpid(id, nullptr, 0);
            //     (void)rid;
            // }

            ThreadData *td = new ThreadData(sockfd, addr, this);
            // version2：多线程版本
            pthread_t tid;
            pthread_create(&tid, nullptr, Routine, td);
        }

        _isrunning = false;
    }

    ~TcpServer() {}

private:
    uint16_t _port;
    int _listensockfd; // 监听套接字
    bool _isrunning;
};